package com.wuyanzu.shixicoderedo.common.utils.jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;

@Component
public class JwtUtil {
    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);

    @Value("${app.jwt.secret}")
    private String secretConfig;

    @Value("${app.jwt.expireSeconds:86400}")
    private long expireSeconds;

    private Key signingKey;

    @PostConstruct
    public void init() {
        signingKey = buildKeyFromSecret(secretConfig);
        if (signingKey == null) {
            log.error("JWT signing key build failed. Check app.jwt.secret");
        } else {
            log.info("JWT signing key initialized. secret length={} chars, keyBytes={}",
                    secretConfig == null ? 0 : secretConfig.length(), signingKey.getEncoded().length);
        }
    }

    public String generateToken(Long userId, String username) {
        if (signingKey == null) throw new IllegalStateException("JWT signing key not initialized");
        long now = System.currentTimeMillis();
        Date iat = new Date(now);
        Date exp = new Date(now + expireSeconds * 1000);
        return Jwts.builder()
                .setSubject(String.valueOf(userId))
                .claim("username", username)
                .setIssuedAt(iat)
                .setExpiration(exp)
                .signWith(signingKey, SignatureAlgorithm.HS256)
                .compact();
    }

    public Claims parseToken(String token) throws JwtException {
        if (signingKey == null) throw new IllegalStateException("JWT signing key not initialized");
        return Jwts.parserBuilder()
                .setSigningKey(signingKey)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    private Key buildKeyFromSecret(String secret) {
        if (secret == null) {
            log.error("JWT secret is null");
            return null;
        }
        byte[] keyBytes = null;
        try {
            if (secret.startsWith("base64:")) {
                keyBytes = Base64.getDecoder().decode(secret.substring("base64:".length()));
            } else {
                // 尝试把字符串当 base64 解码（很多 env 直接存 base64）
                try {
                    keyBytes = Base64.getDecoder().decode(secret);
                    // 如果解码结果很短，会在下方给警告，但继续使用
                } catch (IllegalArgumentException e) {
                    // 非 Base64，尝试 hex 解码（如果是十六进制）
                    if (isHex(secret)) {
                        keyBytes = hexStringToBytes(secret);
                    } else {
                        // 最后退回 raw UTF-8 bytes
                        keyBytes = secret.getBytes(StandardCharsets.UTF_8);
                    }
                }
            }
        } catch (Exception e) {
            log.warn("Failed to decode JWT secret, fallback to UTF-8 bytes: {}", e.getMessage());
            keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        }

        if (keyBytes == null) {
            log.error("Could not obtain bytes from JWT secret");
            return null;
        }
        if (keyBytes.length < 32) {
            log.warn("JWT secret bytes length is < 32 ({}). HS256 recommends at least 32 bytes (256 bits).", keyBytes.length);
            // 仍然尝试构造（可能抛异常）
        }
        try {
            return Keys.hmacShaKeyFor(keyBytes);
        } catch (IllegalArgumentException e) {
            log.error("Keys.hmacShaKeyFor failed: {}", e.getMessage());
            return null;
        }
    }

    private static boolean isHex(String s) {
        return s.matches("^[0-9a-fA-F]+$") && (s.length() % 2 == 0);
    }

    private static byte[] hexStringToBytes(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}
